﻿using System;

namespace Sencha.Serialization
{
	static class JsonUtils
	{
		static readonly float[] pow10s = new float[] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
		static readonly double[] pow10d = new double[] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
		static readonly char[] zerBuff = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', };
		static readonly char[] hexChar = "0123456789ABCDEF".ToCharArray();

		public static string UnescapeAndUnquote(string stringToUnescape)
		{
			if (stringToUnescape == null)
				throw new ArgumentNullException("stringToUnescape");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var start = 0;
			var len = stringToUnescape.Length;

			if (stringToUnescape.Length > 0 && stringToUnescape[0] == '"')
			{
				start += 1;
				len -= 2;
			}

			return UnescapeBuffer(stringToUnescape.ToCharArray(), start, len);
		}
		public static string EscapeAndQuote(string stringToEscape)
		{
			if (stringToEscape == null)
				throw new ArgumentNullException("stringToEscape");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var stringHasNonLatinCharacters = false;
			var newSize = stringToEscape.Length + 2;
			for (int i = 0; i < stringToEscape.Length; i++)
			{
				var charToCheck = stringToEscape[i];
				var isNonLatinOrSpecial = ((int)charToCheck < 32 || charToCheck == '\\' || charToCheck == '"');

				if (isNonLatinOrSpecial)
					newSize += 5; // encoded characters add 4 hex symbols and "u"

				stringHasNonLatinCharacters = stringHasNonLatinCharacters || isNonLatinOrSpecial;
			}

			// if it"s a latin - write as is
			if (!stringHasNonLatinCharacters)
				return string.Concat("\"", stringToEscape, "\"");

			// else tranform and write
			var sb = new System.Text.StringBuilder(newSize);
			var hexBuff = new char[12]; // 4 for zeroes and 8 for number

			sb.Append('"');
			for (int i = 0; i < stringToEscape.Length; i++)
			{
				var charToCheck = stringToEscape[i];

				if ((int)charToCheck < 32 || charToCheck == '\\' || charToCheck == '"')
				{
					sb.Append("\\u");
					Buffer.BlockCopy(zerBuff, 0, hexBuff, 0, sizeof(char) * 8); // clear buffer with "0"
					var hexlen = UInt32ToHexBuffer((uint)charToCheck, hexBuff, 4);
					sb.Append(hexBuff, hexlen, 4);
				}
				else
					sb.Append(charToCheck);
			}
			sb.Append('"');

			return sb.ToString();
		}

		public static int EscapeBuffer(string stringToEscape, int stringOffset, char[] outputBuffer, int outputBufferOffset, char[] tmpBuffer)
		{
			if (stringToEscape == null)
				throw new ArgumentNullException("stringToEscape");
			if (stringOffset < 0 || stringOffset >= stringToEscape.Length)
				throw new ArgumentOutOfRangeException("stringOffset");
			if (outputBuffer == null)
				throw new ArgumentNullException("outputBuffer");
			if (outputBufferOffset < 0 || outputBufferOffset >= outputBuffer.Length)
				throw new ArgumentOutOfRangeException("outputBufferOffset");
			if (tmpBuffer == null)
				throw new ArgumentNullException("tmpBuffer");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const ushort LowerBoundChar = 32;
			const ushort QuoteChar = '\\';
			const ushort DoubleQuoteChar = '"';

			// copy chart to working buffer
			var tmpBufferLength = Math.Min(tmpBuffer.Length, stringToEscape.Length - stringOffset);
			stringToEscape.CopyTo(stringOffset, tmpBuffer, 0, tmpBufferLength);

			var originalOutputOffset = outputBufferOffset;
			// iterate thro chars
			for (int i = 0; i < tmpBuffer.Length; i++)
			{
				if (i >= tmpBufferLength)
					break;

				var value = (ushort)tmpBuffer[i];
				if (value < LowerBoundChar || value == QuoteChar || value == DoubleQuoteChar)
				{
					if (outputBuffer.Length - outputBufferOffset < 6)
						return outputBufferOffset - originalOutputOffset;

					outputBuffer[outputBufferOffset++] = '\\';
					outputBuffer[outputBufferOffset++] = 'u';
					outputBufferOffset += UInt16ToPaddedHexBuffer(value, outputBuffer, outputBufferOffset);
					//outputBufferOffset += 6;
				}
				else
				{
					if (outputBuffer.Length - outputBufferOffset == 0)
						return outputBufferOffset - originalOutputOffset;

					// dont escape
					outputBuffer[outputBufferOffset] = tmpBuffer[i];
					outputBufferOffset++;
				}
			}

			return outputBufferOffset - originalOutputOffset;
		}
		public static string UnescapeBuffer(char[] charsToUnescape, int start, int length)
		{
			if (charsToUnescape == null)
				throw new ArgumentNullException("charsToUnescape");
			if (start < 0 || start + length > charsToUnescape.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var sb = new System.Text.StringBuilder(length);
			var plainStart = start;
			var plainLen = 0;
			var end = start + length;
			for (int i = start; i < end; i++)
			{

				var ch = charsToUnescape[i];
				if (ch == '\\')
				{
					var seqLength = 1;
					// append unencoded chunk
					if (plainLen != 0)
					{
						sb.Append(charsToUnescape, plainStart, plainLen);
						plainLen = 0;
					}

					var seqKind = charsToUnescape[i + 1];
					switch (seqKind)
					{
						case 'n': sb.Append('\n'); break;
						case 'r': sb.Append('\r'); break;
						case 'b': sb.Append('\b'); break;
						case 'f': sb.Append('\f'); break;
						case 't': sb.Append('\t'); break;
						case '\\': sb.Append('\\'); break;
						case '\'': sb.Append('\''); break;
						case '\"': sb.Append('\"'); break;
						// unicode symbol
						case 'u':
							sb.Append((char)HexStringToUInt32(charsToUnescape, i + 2, 4));
							seqLength = 5;
							break;
						// latin hex encoded symbol
						case 'x':
							sb.Append((char)HexStringToUInt32(charsToUnescape, i + 2, 2));
							seqLength = 3;
							break;
						// latin dec encoded symbol
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
						case '0':
							sb.Append((char)StringToInt32(charsToUnescape, i + 1, 3));
							seqLength = 3;
							break;
						default:
#if STRICT
							throw new Exceptions.UnknownEscapeSequence("\\" + seqKind.ToString(), null);
#else
						sb.Append(escapeChar);
						break;
#endif
					}

					// set next chunk start right after this escape
					plainStart = i + seqLength + 1;
					i += seqLength;
				}
				else
					plainLen++;
			}

			// append last unencoded chunk
			if (plainLen != 0)
				sb.Append(charsToUnescape, plainStart, plainLen);

			return sb.ToString();
		}

		public static uint HexStringToUInt32(char[] buffer, int start, int len)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const uint ZERO = (ushort)'0';
			const uint a = (ushort)'a';
			const uint A = (ushort)'A';

			var result = 0u;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				var d = 0u;
				if (c >= '0' && c <= '9')
					d = (c - ZERO);
				else if (c >= 'a' && c <= 'f')
					d = 10u + (c - a);
				else if (c >= 'A' && c <= 'F')
					d = 10u + (c - A);
				else
					throw new FormatException();

				result = 16u * result + d;
			}

			return result;
		}
		public static int UInt32ToHexBuffer(uint uvalue, char[] buffer, int start)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var hex = hexChar;

			if (uvalue == 0)
			{
				buffer[start] = '0';
				return 1;
			}

			var length = 0;
			for (int i = 0; i < 8; i++)
			{
				var c = hex[((uvalue >> i * 4) & 15u)];
				buffer[start + i] = c;
			}

			for (length = 8; length > 0; length--)
				if (buffer[start + length - 1] != '0')
					break;

			Array.Reverse(buffer, start, length);

			return length;
		}

		public static int UInt16ToPaddedHexBuffer(ushort uvalue, char[] buffer, int start)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const int length = 4;
			const string hex = "0123456789ABCDEF";

			var end = start + length;
			if (uvalue == 0)
			{
				for (var i = start; i < end; i++)
					buffer[i] = '0';
				return length;
			}

			for (int i = 0; i < length; i++)
			{
				var c = hex[(int)((uvalue >> i * 4) & 15u)];
				buffer[end - i - 1] = c;
			}


			return length;
		}
		public static ushort PaddedHexStringToUInt16(char[] buffer, int start, int len)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const uint ZERO = (ushort)'0';
			const uint a = (ushort)'a';
			const uint A = (ushort)'A';

			var result = 0u;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				var d = 0u;
				if (c >= '0' && c <= '9')
					d = (c - ZERO);
				else if (c >= 'a' && c <= 'f')
					d = 10u + (c - a);
				else if (c >= 'A' && c <= 'F')
					d = 10u + (c - A);
				else
					throw new FormatException();

				result = 16u * result + d;
			}

			return checked((ushort)result);
		}

		public static long StringToInt64(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const ulong ZERO = (ushort)'0';

			var result = 0UL;
			var neg = false;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (i == 0 && c == '-')
				{
					neg = true;
					continue;
				}
				else if (c < '0' || c > '9')
					throw new FormatException();

				result = checked(10UL * result + (c - ZERO));
			}

			if (neg)
				return -(long)(result);
			else
				return (long)result;
		}
		public static int StringToInt32(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const uint ZERO = (ushort)'0';

			var result = 0u;
			var neg = false;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (i == 0 && c == '-')
				{
					neg = true;
					continue;
				}
				else if (c < '0' || c > '9')
					throw new FormatException();

				result = checked(10u * result + (c - ZERO));
			}

			if (neg)
				return -(int)(result);
			else
				return (int)result;
		}
		public static ulong StringToUInt64(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const ulong ZERO = (ushort)'0';

			var result = 0UL;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (c < '0' || c > '9')
					throw new FormatException();

				result = checked(10UL * result + (c - ZERO));
			}

			return result;
		}
		public static uint StringToUInt32(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const uint ZERO = (ushort)'0';

			var result = 0U;
			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (c < '0' || c > '9')
					throw new FormatException();

				result = checked(10 * result + (c - ZERO));
			}

			return result;
		}
		public static double StringToDouble(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			/*
			const uint ZERO = (ushort)'0';
			char decimalSep = '.';

			var whole = 0UL;
			var fraction = 0U;
			var fracCount = 0;
			var neg = false;
			var decimals = false;

			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (i == 0 && c == '-')
				{
					neg = true;
					continue;
				}
				else if (c == decimalSep)
				{
					decimals = true;
					continue;
				}
				else if (c < '0' || c > '9')
					throw new FormatException();

				if (decimals)
				{
					if (fracCount >= 9) // maximum precision 9 digits
						break;
					fraction = checked(10U * fraction + (c - ZERO));
					fracCount++;
				}
				else
					whole = checked(10UL * whole + (c - ZERO));
			}

			var result = checked((double)whole + (fraction / pow10d[fracCount]));

			if (neg) result = -result;

			return result;
			*/

			return double.Parse(new string(buffer, start, len), formatProvider);
		}
		public static float StringToFloat(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			/*
			const uint ZERO = (ushort)'0';
			char decimalSep = '.';

			var whole = 0U;
			var fraction = 0U;
			var fracCount = 0;
			var neg = false;
			var decimals = false;

			for (var i = 0; i < len; i++)
			{
				var c = buffer[start + i];
				if (i == 0 && c == '-')
				{
					neg = true;
					continue;
				}
				else if (c == decimalSep)
				{
					decimals = true;
					continue;
				}
				else if (c < '0' || c > '9')
					throw new FormatException();

				if (decimals)
				{
					if (fracCount > 9) // maximum precision 9 digits
						break;
					fraction = checked(10U * fraction + (c - ZERO));
					fracCount++;
				}
				else
					whole = checked(10U * whole + (c - ZERO));
			}

			var result = checked((float)whole + (fraction / pow10s[fracCount]));

			if (neg) result = -result;

			return result;
			*/

			return float.Parse(new string(buffer, start, len), formatProvider);
		}
		public static decimal StringToDecimal(char[] buffer, int start, int len, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (len < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + len > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			return decimal.Parse(new string(buffer, start, len), formatProvider);
		}
		public static int Int32ToBuffer(int value, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const int ZERO = (ushort)'0';

			var idx = start;
			var neg = value < 0;
			// Take care of sign
			var uvalue = neg ? (uint)(-value) : (uint)value;
			// Conversion. Number is reversed.
			do buffer[idx++] = (char)(ZERO + (uvalue % 10)); while ((uvalue /= 10) != 0);
			if (neg) buffer[idx++] = '-';

			var length = idx - start;
			// Reverse string
			Array.Reverse(buffer, start, length);

			return length;
		}
		public static int Int64ToBuffer(long value, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const int ZERO = (ushort)'0';

			var idx = start;
			// Take care of sign
			var neg = (value < 0);
			var uvalue = neg ? (ulong)(-value) : (ulong)value;
			// Conversion. Number is reversed.
			do buffer[idx++] = (char)(ZERO + (uvalue % 10)); while ((uvalue /= 10) != 0);
			if (neg) buffer[idx++] = '-';

			var length = idx - start;
			// Reverse string
			Array.Reverse(buffer, start, length);

			return length;
		}
		public static int UInt32ToBuffer(uint uvalue, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const int ZERO = (ushort)'0';

			var idx = start;
			// Take care of sign
			// Conversion. Number is reversed.
			do buffer[idx++] = (char)(ZERO + (uvalue % 10)); while ((uvalue /= 10) != 0);

			var length = idx - start;
			// Reverse string
			Array.Reverse(buffer, start, length);

			return length;
		}
		public static int UInt64ToBuffer(ulong uvalue, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			const ulong ZERO = (ulong)'0';

			var idx = start;
			// Conversion. Number is reversed.
			do buffer[idx++] = (char)(ZERO + (uvalue % 10)); while ((uvalue /= 10) != 0UL);

			var length = idx - start;
			// Reverse string
			Array.Reverse(buffer, start, length);

			return length;
		}
		public static int SingleToBuffer(float value, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var valueStr = value.ToString("R", formatProvider);
			valueStr.CopyTo(0, buffer, start, valueStr.Length);
			return valueStr.Length;
		}
		public static int DoubleToBuffer(double value, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0 || start >= buffer.Length)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var valueStr = value.ToString("R", formatProvider);
			valueStr.CopyTo(0, buffer, start, valueStr.Length);
			return valueStr.Length;
		}
		public static int DecimalToBuffer(decimal value, char[] buffer, int start, IFormatProvider formatProvider = null)
		{
			var valueStr = value.ToString(null, formatProvider);
			valueStr.CopyTo(0, buffer, start, valueStr.Length);
			return valueStr.Length;
		}

		public static ToType ConvertFromBuffer<ToType>(char[] buffer, int start, int length, IFormatProvider format)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			if (length < 0)
				throw new ArgumentOutOfRangeException("len");
			if (start + length > buffer.Length)
				throw new ArgumentOutOfRangeException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

#if STRICT
			format = System.Globalization.CultureInfo.InvariantCulture;
#else
			if (format == null)
				format = System.Globalization.CultureInfo.InvariantCulture;
#endif
			checked
			{
				var result = default(ToType);
				if (result is bool)
				{
					var @bool = default(bool);
					if (LookupAt(buffer, start, length, "true"))
						@bool = true;
					else if (LookupAt(buffer, start, length, "false"))
						@bool = false;
					else
						throw new FormatException(string.Format("Invalid value '{0}' for boolean type", new string(buffer, start, length)));
#if NO_TYPE_REFS
					result = (ResultT)(object)@bool;
#else
					result = __refvalue( __makeref(@bool),ToType);
#endif
				}
				else if (result is byte)
				{
					var @byte = (byte)JsonUtils.StringToUInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@byte;
#else
					result = __refvalue( __makeref(@byte),ToType);
#endif
				}
				else if (result is sbyte)
				{
					var @sbyte = (sbyte)JsonUtils.StringToInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@sbyte;
#else
					result = __refvalue( __makeref(@sbyte),ToType);
#endif
				}
				else if (result is short)
				{
					var @short = (short)JsonUtils.StringToInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@short;
#else
					result = __refvalue( __makeref(@short),ToType);
#endif
				}
				else if (result is ushort)
				{
					var @ushort = (ushort)JsonUtils.StringToUInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@ushort;
#else
					result = __refvalue( __makeref(@ushort),ToType);
#endif
				}
				else if (result is int)
				{
					var @int = (int)JsonUtils.StringToInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@int;
#else
					result = __refvalue( __makeref(@int),ToType);
#endif
				}
				else if (result is uint)
				{
					var @uint = (uint)JsonUtils.StringToUInt32(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@uint;
#else
					result = __refvalue( __makeref(@uint),ToType);
#endif
				}
				else if (result is long)
				{
					var @long = (long)JsonUtils.StringToInt64(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@long;
#else
					result = __refvalue( __makeref(@long),ToType);
#endif
				}
				else if (result is ulong)
				{
					var @ulong = (ulong)JsonUtils.StringToUInt64(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@ulong;
#else
					result = __refvalue( __makeref(@ulong),ToType);
#endif
				}
				else if (result is float)
				{
					var @float = (float)JsonUtils.StringToFloat(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@float;
#else
					result = __refvalue( __makeref(@float),ToType);
#endif
				}
				else if (result is double)
				{
					var @double = (double)JsonUtils.StringToDouble(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@double;
#else
					result = __refvalue( __makeref(@double),ToType);
#endif
				}
				else if (result is decimal)
				{
					var @decimal = (decimal)JsonUtils.StringToDecimal(buffer, start, length, format);
#if NO_TYPE_REFS
					result = (ResultT)(object)@decimal;
#else
					result = __refvalue( __makeref(@decimal),ToType);
#endif
				}
				else
				{
					result = TypeConvert.Convert<string, ToType>(new string(buffer, start, length), format);
				}

				return result;
			}
		}
		public static int ConvertToBuffer<FromType>(char[] buffer, int start, FromType value, IFormatProvider format)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (start < 0)
				throw new ArgumentOutOfRangeException("start");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

#if STRICT
			format = System.Globalization.CultureInfo.InvariantCulture;
#else
			if (format == null)
				format = System.Globalization.CultureInfo.InvariantCulture;
#endif

			checked
			{
				var written = 0;
				if (value is byte)
				{
#if NO_TYPE_REFS
					var @uint32 = System.Convert.ToUInt32(value);
#else
					var @uint32 = (uint) __refvalue( __makeref(value),byte);
#endif

					written = JsonUtils.UInt32ToBuffer(@uint32, buffer, start, format);
				}
				else if (value is sbyte)
				{
#if NO_TYPE_REFS
					var @int32 = System.Convert.ToInt32(value);
#else
					var @int32 = (int) __refvalue( __makeref(value),sbyte);
#endif
					written = JsonUtils.Int32ToBuffer(@int32, buffer, start, format);
				}
				else if (value is short)
				{
#if NO_TYPE_REFS
					var @int32 = System.Convert.ToInt32(value);
#else
					var @int32 = (int) __refvalue( __makeref(value),short);
#endif
					written = JsonUtils.Int32ToBuffer(@int32, buffer, start, format);
				}
				else if (value is ushort)
				{
#if NO_TYPE_REFS
					var @uint32 = System.Convert.ToUInt32(value);
#else
					var @uint32 = (uint) __refvalue( __makeref(value),ushort);
#endif
					written = JsonUtils.UInt32ToBuffer(@uint32, buffer, start, format);
				}
				else if (value is int)
				{
#if NO_TYPE_REFS
					var @int32 = System.Convert.ToInt32(value);
#else
					var @int32 = (int) __refvalue( __makeref(value),int);
#endif
					written = JsonUtils.Int32ToBuffer(@int32, buffer, start, format);
				}
				else if (value is uint)
				{
#if NO_TYPE_REFS
					var @uint32 = System.Convert.ToUInt32(value);
#else
					var @uint32 = (uint) __refvalue( __makeref(value),uint);
#endif
					written = JsonUtils.UInt32ToBuffer(@uint32, buffer, start, format);
				}
				else if (value is long)
				{
#if NO_TYPE_REFS
					var @int64 = System.Convert.ToInt64(value);
#else
					var @int64 = (long) __refvalue( __makeref(value),long);
#endif

					written = JsonUtils.Int64ToBuffer(@int64, buffer, start, format);
				}
				else if (value is ulong)
				{
#if NO_TYPE_REFS
					var @uint64 = System.Convert.ToUInt64(value);
#else
					var @uint64 = (ulong) __refvalue( __makeref(value),ulong);
#endif
					written = JsonUtils.UInt64ToBuffer(@uint64, buffer, start, format);
				}
				else if (value is float)
				{
#if NO_TYPE_REFS
					var @float = System.Convert.ToSingle(value);
#else
					var @float = (float) __refvalue( __makeref(value),float);
#endif
					written = JsonUtils.SingleToBuffer(@float, buffer, start, format);
				}
				else if (value is double)
				{
#if NO_TYPE_REFS
					var @double = System.Convert.ToDouble(value);
#else
					var @double = (double) __refvalue( __makeref(value),double);
#endif
					written = JsonUtils.DoubleToBuffer(@double, buffer, start, format);
				}
				else if (value is decimal)
				{
#if NO_TYPE_REFS
					var @decimal = System.Convert.ToDecimal(value);
#else
					var @decimal = (decimal) __refvalue( __makeref(value),decimal);
#endif
					written = JsonUtils.DecimalToBuffer(@decimal, buffer, start, format);
				}
				else
				{
					var @stringValue = TypeConvert.Convert<FromType, string>(value, format); ;
					@stringValue.CopyTo(0, buffer, start, @stringValue.Length);
					written = @stringValue.Length;
				}

				return written;
			}
		}

		private static bool LookupAt(char[] buffer, int start, int len, string matchString)
		{
			for (var i = 0; i < len; i++)
			{
				if (buffer[start + i] != matchString[i])
					return false;
			}
			return true;
		}
	}
}
